import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class PrintBuffer extends Thread {
	
	private static PriorityBlockingQueue<Message> messageQueue = new PriorityBlockingQueue<Message>();
	//Flush Flag
	private static AtomicBoolean flush = new AtomicBoolean(false);
	//Kill Flag
	private static AtomicBoolean kill = new AtomicBoolean(false);

	/**
	 * Instantiate the PrintBuffer
	 */
	public PrintBuffer() {
		super();
	}

	public void run() {
		while (!kill.get() || !messageQueue.isEmpty()) {
			// Is the flush flag set?
			if (flush.get()) {
				// Start getting messages
				Message m = messageQueue.poll();
				
				// Loop until we have no more messages to process
				while (m != null) {
					// Print out the message
					System.out.println(m.getMessage());
					// Take the next element in the queue
					m = messageQueue.poll();
				}
				// We are out of messages to process, reset the flush flag
				flush.set(false);

				// We are empty so check to see if it's our last hurah
				if (kill.get()) {
					return;
				}
			}
			// Wait until a flush or something else happens
			synchronized (this) {
				try {
					wait();
				} catch (InterruptedException e) {
				}
			}
		}
	}

	public void flush() {
		flush.set(true);
		synchronized (this) {
			notifyAll();
		}
	}

	/**
	 * Kill the PrintBuffer service, flushing out all the contents before doing
	 * so. This must be called at the end of the program.
	 */
	public void kill() {
		kill.set(true);
		flush();
	}

	/**
	 * Add a message to be printed, the message will be printed to System.out
	 * whenever this buffer is flushed out or killed. Messages will be
	 * guaranteed to be printed in the right order as long as they are sent
	 * between buffer flushes.
	 * 
	 * @param t
	 *            The time in the time-stamp, this is used to make sure messages
	 *            are printed in the correct order
	 * @param message
	 *            The message to print out
	 */
	public static void printMessage(Time t, String message) {
		Message m = new Message(t, message);
		messageQueue.offer(m);
	}

	/**
	 * Message Class
	 * 
	 * This class is just a model that holds the information used for the
	 * PrintBuffer. This also takes care of the information being printed in the
	 * correct order by implementing Comparable<Message>
	 * 
	 */
	private static class Message implements Comparable<Message> {
		private Time time;
		private String message;

		public Message(Time t, String message) {
			this.time = t;
			this.message = message;
		}

		public String getMessage() {
			return message;
		}

		@Override
		public int compareTo(Message o) {
			if (time.compareTo(o.time) == 0) {
				return message.compareTo(o.message);
			} else {
				return time.compareTo(o.time);
			}
		}
	}

}
